package com.lemon.exam.common.config;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.github.xiaoymin.knife4j.spring.configuration.Knife4jProperties;
import com.lemon.exam.common.constant.ApiConst;
import com.lemon.exam.common.formatter.LocalDateFormatter;
import com.lemon.exam.common.formatter.LocalDateTimeFormatter;
import com.lemon.exam.common.formatter.LocalTimeFormatter;
import com.lemon.exam.common.properties.ConfigProperties;
import com.lemon.exam.common.properties.ConfigProperties.FileUpload;
import com.lemon.exam.common.properties.ConfigProperties.HttpAllowed;
import com.lemon.exam.common.resolver.NestedParamResolver;
import org.springframework.boot.autoconfigure.AutoConfigureBefore;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.format.FormatterRegistry;
import org.springframework.http.codec.ServerCodecConfigurer;
import org.springframework.http.codec.json.Jackson2JsonDecoder;
import org.springframework.http.codec.json.Jackson2JsonEncoder;
import org.springframework.lang.NonNull;
import org.springframework.validation.Validator;
import org.springframework.validation.beanvalidation.LocalValidatorFactoryBean;
import org.springframework.web.reactive.config.CorsRegistry;
import org.springframework.web.reactive.config.EnableWebFlux;
import org.springframework.web.reactive.config.ResourceHandlerRegistry;
import org.springframework.web.reactive.config.WebFluxConfigurer;
import org.springframework.web.reactive.result.method.annotation.ArgumentResolverConfigurer;

import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.util.Map;

/**
 * WebFlux 配置类，配置跨域配置，静态资源映射，格式化器配置，消息转换器配置
 *
 * @author Lemon
 * @since 2025/3/20 12:10
 */
@Configuration
@EnableWebFlux
@AutoConfigureBefore({Knife4jConfiguration.class, ConfigProperties.class, JacksonConfiguration.class, ObjectMapper.class})
public class WebFluxConfiguration implements WebFluxConfigurer {
    private final ConfigProperties configProperties;
    private final Knife4jProperties knife4jProperties;
    private final ObjectMapper objectMapper;

    @SuppressWarnings("all")
    public WebFluxConfiguration(ConfigProperties configProperties, Knife4jProperties knife4jProperties, ObjectMapper objectMapper) {
        this.configProperties = configProperties;
        this.knife4jProperties = knife4jProperties;
        this.objectMapper = objectMapper;
    }

    /**
     * 跨域配置
     *
     * @param registry
     */
    @Override
    public void addCorsMappings(@NonNull CorsRegistry registry) {
        HttpAllowed allowed = configProperties.getAllowed();
        registry.addMapping(allowed.getMapping())
                .allowedHeaders(allowed.getHeadersArray())
                .allowCredentials(allowed.getCredential()) //允许凭证请求
                .allowedMethods(allowed.getMethodsArray())
                .allowedOrigins(allowed.getOriginsArray());
    }

    /**
     * 静态资源映射
     *
     * @param registry
     */
    @Override
    public void addResourceHandlers(@NonNull ResourceHandlerRegistry registry) {
        //1.匹配上传文件映射
        FileUpload fileUpload = configProperties.getFileUpload();
        registry.addResourceHandler(fileUpload.getMatch())
                .addResourceLocations(fileUpload.getLocationPath());

        //2.添加 Knife4j 的静态资源映射（如果是生产环境就不添加）
        if (!knife4jProperties.isProduction()) {
            Map<String, String> knife4jStaticResources = configProperties.getKnife4jStaticResources();
            registry.addResourceHandler(knife4jStaticResources.keySet().toArray(ApiConst.EMPTY_STRING_ARRAY))
                    .addResourceLocations(knife4jStaticResources.values().toArray(ApiConst.EMPTY_STRING_ARRAY));
        }

    }

    /**
     * 格式化器
     *
     * @param registry
     */
    @Override
    public void addFormatters(@NonNull FormatterRegistry registry) {
        //日期时间格式化器
        registry.addFormatterForFieldType(LocalDateTime.class, new LocalDateTimeFormatter());
        //日期格式化器
        registry.addFormatterForFieldType(LocalDate.class, new LocalDateFormatter());
        //时间格式化器
        registry.addFormatterForFieldType(LocalTime.class, new LocalTimeFormatter());
    }

    /**
     * 配置消息转换器
     *
     * @param configurer
     */
    @Override
    public void configureHttpMessageCodecs(@NonNull ServerCodecConfigurer configurer) {
        // 启用或禁用默认的编解码器
        configurer.defaultCodecs().enableLoggingRequestDetails(true);

        // 使用自定义的 ObjectMapper 替换默认的 JSON 编解码器
        configurer.defaultCodecs().jackson2JsonEncoder(new Jackson2JsonEncoder(objectMapper));
        configurer.defaultCodecs().jackson2JsonDecoder(new Jackson2JsonDecoder(objectMapper));
    }

    /**
     * 添加嵌套参数解析器
     *
     * @param configurer
     */
    @Override
    public void configureArgumentResolvers(ArgumentResolverConfigurer configurer) {
        configurer.addCustomResolver(new NestedParamResolver(objectMapper));// 嵌套参数解析器
    }

    /**
     * 验证器
     *
     * @return
     */
    @Bean
    public Validator validator() {
        return new LocalValidatorFactoryBean();
    }

    @Override
    @NonNull
    public Validator getValidator() {
        return validator();
    }
}
